package com.vagavaga.tcpt;

/**
 * Utility for dumping byte arrays to strings in familiar format.
 * @author River
 * @version 1.0
 */
public final class HexDumper {

    private HexDumper() {
        // no instantiation.
    }

    /**
     * Dumps {@link ByteArrayBuffer} and returns result in string.
     * @param buffer data to dump.
     * @return hex dump of buffer content.
     * @see #dump(byte[])
     */
    public static String dump(ByteArrayBuffer buffer) {
        return dump(buffer.getBytes());
    }

    /**
     * Dumps string content as byte array.
     * @param str string to dump.
     * @return hex dump.
     * @see #dump(byte[])
     */
    public static String dump(String str) {
        return dump(str.getBytes());
    }

    /**
     * Dumps content of string buffer as byte array.
     * @param buffer buffer to dump.
     * @return hex dump.
     * @see #dump(byte[])
     */
    public static String dump(StringBuffer buffer) {
        return dump(buffer.toString());
    }

    /**
     * Dumps content of byte array. This method uses default values for
     * label offset (8), column count (2) and column widht (8).
     * @param b byte array to dump.
     * @return hex dump of array.
     */
    public static String dump(byte[] b) {
        return dump(b, 8, 2, 8);
    }

    /**
     * Dumps byte array to string, using hex formating, witch mirrors hex
     * formating of popular hex editors.
     *
     * @param b byte array to dump
     * @param lblWidth Width of offset label. If lblWidth is greater
     * than 0 then offset label will be added to begining of every line.
     * @param colCount Number of columns in resulting view.
     * @param colWidth Width in bytes of columns.
     * @return String containg formated hex dump of byte array.
     */
    public static String dump(byte[] b, int lblWidth, int colCount,
                              int colWidth) {

        StringBuffer sb = new StringBuffer();
        int pos = 0;
        while (pos < b.length) {
            // print label first
            if (lblWidth > 0) {
                sb.append(fillLeft(Integer.toHexString(pos), lblWidth, '0'));
                sb.append(':');
            }

            StringBuffer line = new StringBuffer(colCount * colWidth * 2
                    + lblWidth);
            StringBuffer text = new StringBuffer(colCount * colWidth * 2
                    + lblWidth);
            // next print columns
            for (int col = 0; col < colCount; col++) {
                // Calculate start byte position of this column
                int startPos = pos + col * colWidth;
                int endPos = startPos + colWidth;
                while (startPos < b.length && startPos < endPos) {
                    line.append(' ');
                    line.append(HexEncoder.byteToHex(b[startPos]));
                    text.append((char) b[startPos]);
                    startPos++;
                }
                // next we need to fill until end of line
                while (startPos < endPos) {
                    // 3 blank chars
                    line.append("   ");
                    startPos++;
                }
                // next add separator character to end of column
                line.append(" |");
            }
            sb.append(line);
            sb.append(' ');
            for (int i = 0; i < text.length(); i++) {
                if (text.charAt(i) < 32) {
                    sb.append((char) 0);
                } else {
                    sb.append(text.charAt(i));
                }
            }
            sb.append('\n');
            pos += colWidth * colCount;
        }
        return sb.toString();
    }

    /**
     * Utilitiy function for filling strings.
     * @param input string to fill.
     * @param len size of new string that we want.
     * @param ch character to fill with.
     * @return filled string.
     */
    private static String fillLeft(String input, int len, char ch) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < len - input.length(); i++) {
            sb.append(ch);
        }
        sb.append(input);
        return sb.toString();
    }
}
